package cn.com.bluemoon.daps.sync.core.sync;

import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.Predicate;

/**
 * 实体对比工具
 *
 * @author Jarod.Kong
 * @date 2021/6/3 17:04
 */
public class EntityCompareUtils {

    /**
     * @param obj1            进行属性比较的对象1
     * @param obj2            进行属性比较的对象2
     * @param inclusionFields 选择只要比较的属性数组
     * @return 属性差异比较结果 map
     */
    public static <T> Map<String, List<Object>> compareFields(T obj1, T obj2,
                                                              String[] inclusionFields) {
        Set<String> fieldSet = new HashSet<>();
        if (inclusionFields != null && inclusionFields.length > 0) {
            fieldSet.addAll(Arrays.asList(inclusionFields));
        }
        return compareFields(obj1, obj2, new Predicate<String>() {
            @Override
            public boolean test(String fieldName) {
                if (fieldSet.isEmpty()) {
                    return false;
                }
                return fieldSet.contains(fieldName);
            }
        });
    }

    public static <T> Map<String, List<Object>> compareFieldsIgnore(T obj1, T obj2, String[] ignoreFields) {
        Set<String> fieldSet = new HashSet<>();
        if (ignoreFields != null && ignoreFields.length > 0) {
            fieldSet.addAll(Arrays.asList(ignoreFields));
        }
        return compareFields(obj1, obj2, new Predicate<String>() {
            @Override
            public boolean test(String fieldName) {
                if (fieldSet.isEmpty()) {
                    return true;
                }
                return !fieldSet.contains(fieldName);
            }
        });
    }

    @SuppressWarnings("rawtypes")
    public static <T> Map<String, List<Object>> compareFields(T obj1, T obj2, Predicate<String> filter) {
        try {
            if (obj1 == null && obj2 == null) {
                return null;
            }
            if (obj1 != null && obj2 == null) {
                try {
                    //noinspection unchecked
                    obj2 = (T) obj1.getClass().newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            if (obj1 == null && obj2 != null) {
                try {
                    //noinspection unchecked
                    obj1 = (T) obj2.getClass().newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            Map<String, List<Object>> map = new HashMap<String, List<Object>>();
            assert obj1 != null;
            assert obj2 != null;
            BmAssetUtils.isTrue(obj1.getClass() == obj2.getClass(), "只允许相同类型的对象进行值比较");
            //只有两个对象都是同一类型才有可比性
            Class clazz = obj1.getClass();
            //获取object的属性描述
            PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz, Object.class).getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                String name = pd.getName();
                if (filter.test(name)) {
                    Method readMethod = pd.getReadMethod();//获取属性的get方法
                    //在obj1上调用get方法等同于获得obj1的属性值
                    Object o1 = readMethod.invoke(obj1);
                    //在obj2上调用get方法等同于获得obj2的属性值
                    Object o2 = readMethod.invoke(obj2);
                    //如果此属性是时间
                    if (o1 instanceof Timestamp) {
                        o1 = new Date(((Timestamp) o1).getTime());
                    }
                    if (o2 instanceof Timestamp) {
                        o2 = new Date(((Timestamp) o2).getTime());
                    }
                    if (o1 == null && o2 == null) {
                        continue;
                    } else if (o1 == null && o2 != null) {
                        //不一致
                        List<Object> list = new ArrayList<Object>();
                        list.add(o1);
                        list.add(o2);
                        map.put(name, list);
                        continue;
                    }
                    if (!o1.equals(o2)) {
                        List<Object> list = new ArrayList<>();
                        list.add(o1);
                        list.add(o2);
                        map.put(name, list);
                    }
                }
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
